package cn.blackhole.simple.domain;

import cn.blackhole.simple.domain.circuitBreaker.model.objValue.CircuitState;
import cn.blackhole.simple.common.enums.InterfaceType;
import cn.blackhole.simple.common.exception.CircuitBreakerOpenException;
import cn.blackhole.simple.common.exception.RetryExhaustedException;
import cn.blackhole.simple.domain.Metric.model.entity.MetricData;
import cn.blackhole.simple.domain.Metric.model.objValue.MetricType;
import cn.blackhole.simple.domain.Metric.service.MetricsCollector;
import cn.blackhole.simple.domain.circuitBreaker.service.CircuitBreaker;
import cn.blackhole.simple.domain.circuitBreaker.service.FallbackHandler;
import cn.blackhole.simple.domain.exception.service.ExceptionHandler;
import cn.blackhole.simple.domain.factory.PolicyFactory;
import cn.blackhole.simple.domain.level.service.InterfaceTypeDetector;
import cn.blackhole.simple.domain.retry.service.RetryPolicy;
import lombok.extern.slf4j.Slf4j;

import java.net.MalformedURLException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Slf4j
public class HttpEnhancer {

    private final InterfaceTypeDetector typeDetector;

    private final PolicyFactory policyFactory;

    public HttpEnhancer(InterfaceTypeDetector typeDetector, PolicyFactory policyFactory) {
        this.typeDetector = typeDetector;
        this.policyFactory = policyFactory;
    }

    private void recordSuccessMetrics(InterfaceType type, long startTime) {
        long duration = System.currentTimeMillis() - startTime;
        MetricsCollector collector = policyFactory.getMetricsCollector();
        collector.recordMetric(new MetricData("http", type, MetricType.SUCCESS_REQUESTS, 1));
        collector.recordResponseTime(type, duration);
    }

    private void recordFailureMetrics(InterfaceType type, long startTime, Exception e) {
        long duration = System.currentTimeMillis() - startTime;
        MetricsCollector collector = policyFactory.getMetricsCollector();
        collector.recordMetric(new MetricData("http", type, MetricType.FAILED_REQUESTS, 1));
        collector.recordResponseTime(type, duration);
        collector.recordException(type, e);
    }

    public <R> R executeWithRetry(URI uri, Function<URI, R> function) throws MalformedURLException {

        InterfaceType type = typeDetector.detect(uri);

        RetryPolicy retryPolicy = policyFactory.getRetryPolicy(type);

        ExceptionHandler exceptionHandler = policyFactory.getExceptionHandler(type);

        int attempt = 0;
        while (true) {
            attempt++;
            try {
                return function.apply(uri);
            } catch (Exception e) {
                if (!retryPolicy.shouldRetry(e) || attempt >= retryPolicy.maxAttempts()) {
                    if (!exceptionHandler.handleException(e, type)) {
                        throw new RetryExhaustedException("Retry exhausted", e);
                    }
                    return null; // 根据业务需求调整
                }
                try {
                    TimeUnit.of(retryPolicy.getChronoUnit()).sleep(retryPolicy.delayMillis(attempt));
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RetryExhaustedException("Interrupted", ie);
                }
            }
        }
    }

    // 增加熔断和降级处理
    public <R> R executeWithResilience(URI request, Function<URI, R> function) throws MalformedURLException {
        final InterfaceType type = typeDetector.detect(request);
        CircuitBreaker breaker = policyFactory.getCircuitBreaker(type);
        FallbackHandler<R> fallback = policyFactory.getFallbackHandler(type);
        policyFactory.getMetricsCollector().recordMetric(new MetricData("http", type, MetricType.TOTAL_REQUESTS, 1));
        if (!breaker.allowRequest()) {
            return handleCircuitOpen(request, fallback, breaker);
        }
        long startTime = System.currentTimeMillis();
        recordSuccessMetrics(type, startTime);
        try {
            R result = executeWithRetry(request, function);
            breaker.recordSuccess();
            return result;
        } catch (Exception e) {
            recordFailureMetrics(type, startTime, e);
            breaker.recordFailure();
            return handleFallback(request, fallback, e, breaker);
        }
    }

    private <T> T handleCircuitOpen(URI uri, FallbackHandler<T> fallback, CircuitBreaker breaker) {
        if (breaker.getState() == CircuitState.HALF_OPEN) {
            log.info("Circuit in HALF_OPEN state, allowing trial request");
            return null; // 特殊处理逻辑
        }
        log.info("Circuit breaker OPEN, executing fallback");
        return fallback.handleFallback(uri, new CircuitBreakerOpenException());
    }

    private <T> T handleFallback(URI uri, FallbackHandler<T> fallback, Throwable ex, CircuitBreaker breaker) {
        try {
            return fallback.handleFallback(uri, ex);
        } catch (Exception fallbackEx) {
            if (ex instanceof RetryExhaustedException) {
                breaker.recordFailure();
            }
            throw fallbackEx;
        }
    }

}